class Utils {
	constructor() {
		// 空图片
		this.emptyImg =
			"";
		// 设置基本数据类型
		this.class2type = {};
		this.each(
			"Boolean Number String Function Array Date RegExp Object".split(
				" "
			),
			(item) => {
				this.class2type[`[object ${item}]`] = item.toLowerCase();
			}
		);
		// 绑定正则公式
		this.bindRegs();
	}
	/**
	 * @description 判断数据类型
	 * @summary 参考于$.type
	 * @param {*} value - 需要判断的数据
	 * @returns {string} 字符串
	 * @example
	 * utils.type('xxx') = 'string'
	 */
	type(value) {
		if (value == null) {
			return String(value); // null undefined
		}
		return this.class2type[toString.call(value)] || "object";
	}
	/**
	 * @description 遍历数组或对象
	 * @summary 可在回调中 return false 中止遍历
	 * @summary 参考于$.each
	 * @param {array | object} value - 需要遍历的数据
	 * @param {function} cb - 每次遍历的回调函数，返回两个参数，遍历到的值和下标
	 * @example
	 * utils.each([1, 2, 3], (item, index) => {})
	 */
	each(value, cb) {
		if (value.length) {
			for (let i = 0, len = value.length; i < len; i++) {
				if (cb.call(value[i], value[i], i) === false) {
					break;
				}
			}
		} else {
			for (let item in value) {
				if (cb.call(value[item], value[item], item) === false) {
					break;
				}
			}
		}
		return value;
	}
	/**
	 * @description 判断是否为空
	 * @param {*} value - 需要判断的数据
	 * @returns {boolean} 布尔值
	 * @example
	 * utils.isEmpty('xxx') = false
	 */
	isEmpty(value) {
		switch (this.type(value)) {
			case "object":
				for (var i in value) {
					return false;
				}
				return true;
			case "array":
				return !value.length;
			case "string":
				// ''或' '认为为空
				return !value.trim();
			case "boolean":
				// true则不为空
				return !value;
			case "number":
				// 包括数字，Infinity，NaN
				return Number.isNaN(value);
			case "null":
			case "undefined":
				return true;
			case "function":
			case "date":
			case "regexp":
			default:
				// 其余格式默认不为空
				return false;
		}
	}
	/**
	 * @description 参数批量校验
	 * @param {object} rules - 校验规则
	 * @param {object} params - 校验参数
	 * @returns {string} 字符串
	 * @example
	 * utils.verify({
	 *      name: [
	 *          "请输入name",
	 *          {
	 *              fn: (value) => {
	 *                  if (value > 0) return true;
	 *                  return false;
	 *              },
	 *              tips: 'name需要大于0'
	 *          },
	 *          {
	 *              reg: /^0?(1[3456789])[0-9]{9}$/,
	 *              tips: 'name正则校验失败'
	 *          }
	 *      ]
	 * })
	 */
	verify(rules, params) {
		let tips = "";
		this.each(rules, (rule, key) => {
			this.each(rule, (item) => {
				if (!(key in params)) {
					// 该字段未匹配到值，不做校验
					return false;
				}
				let value = params[key];
				if (this.type(item) === "string") {
					// 为空校验
					if (this.isEmpty(value)) {
						tips = item;
						return false;
					}
				} else if (this.type(item) === "object") {
					// 自定义校验
					if (this.type(item.fn) === "function") {
						if (!item.fn(value)) {
							tips = item.tips;
							return false;
						}
					}
					// 正则校验
					if (item.reg) {
						if (!item.reg.test(value)) {
							tips = item.tips;
							return false;
						}
					}
				}
			});
			if (tips) {
				return false;
			}
		});
		return tips;
	}
	/**
	 * @description 格式化时间
	 * @param {*} date - 时间
	 * @param {string} format - 格式化形式，以下为字段描述，可根据个数控制位数
	 * @param {string} format.yyyy 年
	 * @param {string} format.MM 月
	 * @param {string} format.dd 日
	 * @param {string} format.HH 时 24小时制
	 * @param {string} format.hh 时 12小时制
	 * @param {string} format.mm 分
	 * @param {string} format.ss 秒
	 * @param {string} format.S 毫秒
	 * @param {string} format.EEE 星期
	 * @param {string} format.qq 季节 - 废弃
	 * @returns {string} 字符串
	 * @example
	 * utils.dateFormat(2016-06-10 11:21:36, "yyyy年MM月dd日 hh时mm分ss秒")
	 */
	dateFormat(date, format = "yyyy-MM-dd HH:mm:ss") {
		let type = this.type(date);
		if (type !== "date") {
			if (type === "string") {
				date = new Date(date.replace(/-/g, "/"));
			} else if (type === "number") {
				date = new Date(date);
			} else {
				console.error("dateFormat", "时间格式错误");
				return "";
			}
		}
		let time = {
				"y+": date.getFullYear(),
				"M+": date.getMonth() + 1,
				"d+": date.getDate(),
				"H+": date.getHours(),
				"h+": date.getHours() % 12,
				"m+": date.getMinutes(),
				"s+": date.getSeconds(),
				// 'q+': Math.floor((date.getMonth() + 3) / 3), // 四季
				S: date.getMilliseconds(),
			},
			week = {
				"0": "\u65e5", // 日
				"1": "\u4e00", // 一
				"2": "\u4e8c", // 二
				"3": "\u4e09", // 三
				"4": "\u56db", // 四
				"5": "\u4e94", // 五
				"6": "\u516d", // 六
			};
		this.each(time, (item, key) => {
			if (new RegExp("(" + key + ")").test(format)) {
				let $1 = RegExp.$1,
					len = $1.length;
				if (len > 1) {
					// 设置显示多位数时，位数不够补零
					item = (Array(len).join(0) + item).slice(-len);
				}
				format = format.replace($1, item);
			}
		});
		if (/(E+)/.test(format)) {
			let $1 = RegExp.$1,
				len = $1.length,
				text = "";
			if (len >= 2) {
				text = "\u661f\u671f"; // 星期
			} else if (len === 1) {
				text = "\u5468"; // 周
			}
			format = format.replace($1, text + week[date.getDay() + ""]);
		}
		return format;
	}
	/**
	 * @description 格式化数字小数位数，不够补零
	 * @summary 数字位数太长将直接截取，不适用需要四舍五入的数字
	 * @param {number | string}  value - 需要格式化的数字
	 * @param {number} n - 小数点位数
	 * @returns {string} 字符串
	 * @example
	 * utils.numToLen(1.2, 2) = 1.20
	 */
	numToLen(value, n = 2) {
		// 非数据格式
		if (Number.isNaN(+value)) {
			return "-";
		}
		if (!value) {
			return "0." + Array(n + 1).join("0");
		}
		let arr = (value + "").replace(/[^\d.-]/g, "").split("."),
			int = arr[0].replace(/(\d)(?=(?:\d{3})+$)/g, "$1,"),
			float = Array(n + 1).join("0");
		if (arr[1]) {
			float = (arr[1] + Array(n).join("0")).slice(0, n);
		}
		return [int, float].join(".");
	}
	/**
	 * @description 格式化两数相减精度问题
	 * @param {number} arg1 - 减数
	 * @param {number} arg2 - 被减数
	 * @returns {number} 数字
	 * @example
	 * utils.subtrAccuracy(0.1, 0.01) = 0.09
	 */
	subtrAccuracy(arg1, arg2) {
		let len1, len2, max, ten;
		try {
			len1 = (arg1 + "").split(".")[1].length;
		} catch (e) {
			len1 = 0;
		}
		try {
			len2 = (arg2 + "").split(".")[1].length;
		} catch (e) {
			len2 = 0;
		}
		max = Math.max(len1, len2); // 动态控制精度长度
		ten = Math.pow(10, max);
		return ((arg1 * ten - arg2 * ten) / ten).toFixed(max);
	}
	/**
	 * @description 正则校验
	 * @example
	 * utils.isTel.test('xxx') = false
	 */
	bindRegs() {
		this.each(
			// 各种正则规则
			{
				isTel: /^0?(1[3456789])[0-9]{9}$/, // 手机号码
				isEmail: /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, // 邮箱格式
				isPassword: /^([a-zA-Z0-9]|[._]){8,20}$/, // 密码
				isIdcard: /^([1-9]{1})(\d{15,18})$/, // 银行卡
				isInt: /^-?\d+$/, // 是否整数
				isNumber: /^-?\d*\.?\d+$/, // 数字验证，包含正负、小数
				isIdcode: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, // 简单验证身份证
				isUrl: /^(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/, // 网址
				isQQ: /^[1-9][0-9]{4,}$/, // QQ
				isWeChat: /^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/, // 微信
				isCn: /^[\u4E00-\u9FA5]+$/, // 纯中文
				isEn: /^[a-zA-Z]+$/, // 纯英文
				isCarpat: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/, // 华夏车牌号
				isIpv4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, // ipv4
				isIpv6: /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/, // ipv6
				isHex: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/, // hex验证
			},
			(item, key) => {
				this[key] = item;
			}
		);
	}
	/**
	 * @todo 可用，待优化
	 */
	createBlank(url, name) {
		if (!url) {
			alert("新开窗口链接不存在");
		}
		const a = document.createElement("a");
		a.target = "_blank";
		if (name) {
			a.setAttribute("download", name);
		}
		a.href = url;
		if (document.createEvent) {
			var evt = document.createEvent("MouseEvents");
			evt.initEvent("click", true, true);
			a.dispatchEvent(evt);
		} else if (a.fireEvent) {
			a.fireEvent("onclick");
		} else if (a.click) {
			if (typeof a.onclick == "function") {
				a.onclick({
					type: "click",
				});
				a.click();
			} else {
				window.open(url, "_blank");
			}
		} else {
			window.open(url, "_blank");
		}
	}
}

export default new Utils();
